﻿using System;

namespace HYGE.AsyncCommand
{
    /// <summary>
    /// CanExecuteChangedのPush通知を実現するIAsyncCommand実装の抽象基底クラス。
    /// </summary>
    public abstract class ReactiveAsyncCommandBase : AsyncCommandBase
    {
        #region Constructors

        #region Default
        /// <summary>
        /// デフォルトコンストラクタ。
        /// </summary>
        /// <param name="canExecuteSource"></param>
        /// <param name="initialCanExecute"></param>
        public ReactiveAsyncCommandBase(IObservable<bool> canExecuteSource = null, bool initialCanExecute = false)
        {
            if (canExecuteSource == null)
                this.canExecute = true;
            else
            {
                var observer = new CanExecuteObserver(this);
                canExecuteSource.Subscribe(observer);
                observer.OnNext(initialCanExecute);
            }
        }
        #endregion

        #endregion

        #region OverrideMethods

        #region CanExecute
        private bool canExecute = false;
        /// <summary>
        /// AsyncCommandBase.CanExecute(object)のオーバーライド。
        /// </summary>
        /// <param elementName="parameter"></param>
        /// <returns></returns>
        protected override bool CanExecute(object parameter)
        {
            return (this.canExecute);
        }
        #endregion

        #endregion

        #region InnerClasses

        #region CanExecuteObserver
        /// <summary>
            /// IObservable{bool}の通知から、指定されたReactiveAsyncCommandBaseのCanExecuteChangedを発行するIObserver。
        /// </summary>
        private class CanExecuteObserver : IObserver<bool>
        {
            #region Fields

            #region reactiveAsyncCommand
            /// <summary>
            /// コンストラクタで指定されたReactiveAsyncCommandBase。
            /// 通知があった場合、このコマンドのICommand.CanExecuteChangedを発行する。
            /// </summary>
            private ReactiveAsyncCommandBase reactiveAsyncCommand = null;
            #endregion

            #endregion

            #region IObserverMembers

            #region Methods

            #region OnCompleted
            /// <summary>
            /// IObserver.OnCompletedの実装。
            /// 通知が完了した場合に実行される。
            /// 使用しないので未実装。
            /// </summary>
            public void OnCompleted()
            {
                throw (new NotImplementedException("CanExecuteObserver.OnCompleted"));
            }
            #endregion

            #region OnError
            /// <summary>
            /// IObserver.OnErrorの実装。
            /// 通知内でエラーがあった場合に実行される。
            /// 使用しないので未実装。
            /// </summary>
            /// <param name="error"></param>
            public void OnError(Exception error)
            {
                throw (new NotImplementedException("CanExecuteObserver.OnError"));
            }
            #endregion

            #region OnNext
            /// <summary>
            /// IObserver.OnNextの実装。
            /// 通知された場合に実行される。
            /// コンストラクタで指定されたSubscriptionCommandBaseのCanExecuteChangedを発行する。
            /// </summary>
            /// <param name="value"></param>
            public void OnNext(bool value)
            {
                if (this.reactiveAsyncCommand.canExecute == value)
                    return;
                this.reactiveAsyncCommand.canExecute = value;
                this.reactiveAsyncCommand.OnCanExecuteChanged();
            }
            #endregion

            #endregion

            #endregion

            #region Constructors

            #region Default
            /// <summary>
            /// デフォルトコンストラクタ。
            /// </summary>
            /// <param name="reactiveAsyncCommand"></param>
            public CanExecuteObserver(ReactiveAsyncCommandBase reactiveAsyncCommand)
            {
                //引数チェック
                if (reactiveAsyncCommand == null)
                    throw (new ArgumentNullException("reactiveAsyncCommand"));

                this.reactiveAsyncCommand = reactiveAsyncCommand;
            }
            #endregion

            #endregion
        }
        #endregion

        #endregion
    }
}
